Skip to content

Retry and decrypt-and-retry for recorded failures (Review Flow PR 5) - #7479

Draft
EthanHealy01 wants to merge 5 commits into
feature/failure-notificationsfrom
feature/policy-decrypt-retry
Draft

Retry and decrypt-and-retry for recorded failures (Review Flow PR 5)#7479
EthanHealy01 wants to merge 5 commits into
feature/failure-notificationsfrom
feature/policy-decrypt-retry

Conversation

@EthanHealy01

@EthanHealy01 EthanHealy01 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Flow PR 5. Stacked on #7478. All resolution of a recorded failure lives here: Retry and Decrypt and retry, for editor failures and policy failures alike. #7478 surfaces failures and offers viewing and dismissal; this PR adds the buttons that fix them.

What's added

The retry stash (notificationRetry.ts). When a tool run fails, useToolOperation stashes what a retry needs — endpoint, parameters, file ids — in its own IndexedDB database, keyed on the failing document. Capped at 25 records, oldest evicted first. Password-shaped fields are stripped at any depth on the way in, and the walk is depth-bounded so a pathological or cyclic object fails closed rather than storing a subtree it never examined.

Retry on an editor failure opens the failed tool with the document selected, so the user sees the settings before it runs again.

Decrypt and retry on an editor failure. The row reveals a password field. On submit it re-runs the stashed operation with the password added, adopts the result into the workbench, and reports the row resolved.

Retry and Decrypt and retry on a policy failure. The unlock goes through POST /api/v1/security/remove-password, the result is adopted, and the stored policy re-runs on it server-side.

A policy-shaped retry target. RetryTarget is a discriminated union: the tool arm comes from the stash, the policy arm is derived from the notification itself (policyId plus the document reference), so no stash is needed for a policy failure.

Run registration. A policy re-run is recorded with the run store, so it polls to terminal, imports its output honouring the policy's outputMode, and continues the rest of the upload chain rather than running one policy in isolation.

The action slot model. FailureActionSlot ranks each offer as the row's RESOLUTION, SECONDARY or OVERFLOW, and promoteActions turns those into the row's primary and secondary buttons plus the reason for anything withheld. It arrives with this PR because ranking is only a question worth asking once a kind has a resolution to rank — the bell alone offers viewing and dismissal, in declaration order.

Behaviour

  • The unlocked document is added, not consumed: the encrypted original stays in the workbench.
  • Exactly one policy run starts per click. Adoption is marked derivedFromTool so the upload auto-run does not also fire on it, which would bill the customer twice.
  • Failures stop the sequence where they occur. A wrong password reports inline and nothing else runs. A failed adoption neither re-runs nor resolves. A refused re-run leaves the document adopted and does not resolve.
  • A re-run whose output cannot be delivered leaves the row open. If the local policy cache cannot place the policy, the run still fires but nothing polls it, so the row stays open and says so rather than closing on a result that never arrives.
  • The password reaches the network from one place and is never stored, logged, or put in an error message.
  • A failure this browser did not report has no stash entry, so its Retry is simply not offered on other devices.

Not included

Unattended failures (folder, S3, webhook) still cannot be retried: no browser holds the document. Those actions stay disabled with a reason. Re-running from the source needs a ledger requeue transition that does not exist yet.

ToolType.custom tools (Convert, OCR) have no single endpoint to re-submit to, so they get no Retry.

How to test

Needs a proprietary or SaaS build with login enabled, and a stored policy that will fail on a locked document. task dev:all, then sign in.

  1. Create the failure. Add a password-protected PDF to the editor and choose Skip for now. The upload's policy run fails on it.
  2. Open the bell once the badge appears. The row's primary button is now Decrypt and retry — on Surface recorded failures in a notification bell (Review Flow PR 4) #7478 alone it was View file, because the slot ranking and the handler both arrive here.
  3. Press it. A password field appears in the row. Enter the wrong password first: the row reports the server's message inline and stays open.
  4. Enter the correct password. The unlocked document appears in the workbench alongside the still-encrypted original, the policy re-runs on it, and the row leaves the list.
  5. Check the run is tracked. The activity feed shows the re-run progressing to completion and its output arriving in the workbench, rather than a run that never reports.
  6. Confirm only one run per click. In DevTools there is exactly one POST /api/v1/policies/{id}/run for the retry. Two would mean the adopted document also triggered the upload policy.
  7. Confirm nothing leaks the password. Search the request log and IndexedDB (stirling-pdf-retry) for what you typed. It should appear only in the one unlock request body.
  8. Editor retry. Fail a plain tool run (rotate a corrupted file), open the bell, press Retry: the failed tool opens with that document selected.
  9. Confirm the stash strips secrets. After a remove-password failure, inspect IndexedDB stirling-pdf-retry: the stashed parameters contain no password-shaped field.

Migration

None.

@stirlingbot stirlingbot Bot added the has conflicts Pull request has merge conflicts with the base branch label Aug 12, 2026
@EthanHealy01
EthanHealy01 force-pushed the feature/failure-notifications branch 2 times, most recently from dff3d86 to 9c2fa39 Compare August 13, 2026 01:35
@EthanHealy01
EthanHealy01 force-pushed the feature/policy-decrypt-retry branch from 801bd5b to dd5ddaa Compare August 13, 2026 01:46
@stirlingbot stirlingbot Bot removed the has conflicts Pull request has merge conflicts with the base branch label Aug 13, 2026
@EthanHealy01
EthanHealy01 force-pushed the feature/policy-decrypt-retry branch from dd5ddaa to 41772a5 Compare August 13, 2026 15:27
@EthanHealy01 EthanHealy01 changed the title Decrypt a held document and re-run the policy that failed (Review Flow PR 5) Retry and decrypt-and-retry for recorded failures (Review Flow PR 5) Aug 13, 2026
@EthanHealy01
EthanHealy01 force-pushed the feature/failure-notifications branch 2 times, most recently from 29d124e to ae906e7 Compare August 13, 2026 21:29
@EthanHealy01
EthanHealy01 force-pushed the feature/policy-decrypt-retry branch from 41772a5 to 5fb095b Compare August 13, 2026 21:31
@github-actions github-actions Bot added the has conflicts Pull request has merge conflicts with the base branch label Aug 17, 2026
…rying

All resolution of a recorded failure lives here, on top of the bell that
surfaces it:

- The retry stash: what a failed tool run would need to run again (endpoint,
  parameters, file ids), kept client-side with password-shaped fields stripped
  at any depth and a depth-bounded walk that fails closed.
- Editor retry and decrypt-and-retry: re-open the failed tool, or unlock a
  password-protected document and re-run it in place.
- Server policy retry: an attended policy failure is re-run with the password
  the user supplies, exactly once per click.

The action slot model arrives with them. Slots rank an action as the row's
resolution, its runner-up, or overflow, which is only a question worth asking
once a kind has a resolution to rank: the bell alone offers viewing and
dismissal, in declaration order. promoteActions turns those slots into the
row's primary and secondary buttons and reports what it withheld.

A build without this PR still shows every failure; the server declares RETRY
and DECRYPT_AND_RETRY there and a client with no handler for them skips them.
…t-retry

# Conflicts:
#	frontend/editor/public/locales/en-US/translation.toml
Decrypt and retry unlocked the document but left the user holding two
copies of it, and the retry restarted the upload chain from the front
rather than rejoining it where it stopped. The unlock now versions the
original in place via consumeFiles, and the run resumes at the first
policy that has not already been applied.

Read state moves from the id of the newest notification to a watermark
on the time the list is ordered by. Resolving the newest row used to
leave the marker pointing at nothing, which read every remaining row as
unread again.

Each row now shows two buttons and an overflow menu instead of a line of
near-equal ones, and the failure message sits in its own box with copy
and expand as corner glyphs.
@EthanHealy01
EthanHealy01 force-pushed the feature/policy-decrypt-retry branch from 5fb095b to 6539b98 Compare August 20, 2026 01:45
…nto feature/policy-decrypt-retry

# Conflicts:
#	app/proprietary/src/main/java/stirling/software/proprietary/failure/FailureActionId.java
#	app/proprietary/src/main/java/stirling/software/proprietary/failure/FailureKind.java
#	app/proprietary/src/main/java/stirling/software/proprietary/failure/FileRunEventService.java
#	app/proprietary/src/main/java/stirling/software/proprietary/failure/FileRunEventView.java
#	app/proprietary/src/main/java/stirling/software/proprietary/notification/NotificationController.java
#	app/proprietary/src/main/java/stirling/software/proprietary/notification/NotificationService.java
#	app/proprietary/src/main/java/stirling/software/proprietary/notification/NotificationSource.java
#	app/proprietary/src/test/java/stirling/software/proprietary/failure/FailureKindTest.java
#	app/proprietary/src/test/java/stirling/software/proprietary/failure/FileRunEventServiceTest.java
#	frontend/editor/src/core/components/notifications/NotificationBell.test.tsx
#	frontend/editor/src/core/components/notifications/NotificationBell.tsx
#	frontend/editor/src/core/components/notifications/notificationActions.ts
#	frontend/editor/src/core/hooks/useNotifications.test.ts
#	frontend/editor/src/core/hooks/useNotifications.ts
#	frontend/editor/src/core/services/localFilePresence.ts
#	frontend/editor/src/core/services/notifications.ts
#	frontend/editor/src/proprietary/components/notifications/notificationActions.ts
@github-actions github-actions Bot removed the has conflicts Pull request has merge conflicts with the base branch label Aug 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌐 TOML Translation Verification Summary

🔄 Reference Branch: pr-branch

📃 File Check: en-US/translation.toml

  1. Test Status:Passed
  2. Test Status:Passed
  3. Test Status:Passed

✅ Overall Check Status: Success

Thanks @EthanHealy01 for your help in keeping the translations up to date.

@github-actions github-actions Bot deleted a comment from stirlingbot Bot Aug 20, 2026
Three things from this morning's demo:

The row now carries the kind's own sentence rather than the server's raw
message, the empty panel says the reader is caught up, and the log moves
to a Copy log entry in the overflow menu. Reading the stack trace is the
processor's job, not the bell's.

View in processor opens a new tab when the reader has a loaded workbench
to lose, so a failure mid-upload does not cost them every file they had
open. From the processor, or from an empty workbench, it navigates as it
did.

Decrypt and retry versions the encrypted original in place when the file
is merely closed in the sidebar. The parent stub was read only from the
workbench, so a closed file was adopted as a new one: the user ended up
holding a decrypted copy and the locked original side by side.
@github-actions

Copy link
Copy Markdown
Contributor

🚀 V2 Auto-Deployment Complete!

🔗 Direct Test URL (non-SSL) http://54.175.155.236:7479

🧩 Admin portal included - try it at http://54.175.155.236:7479/portal.

This deployment will be automatically cleaned up when the PR is closed.

🔄 Auto-deployed for approved V2 contributors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant